package com.atguigu.exam.service.impl;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.atguigu.exam.entity.PaperQuestion;
import com.atguigu.exam.entity.Question;
import com.atguigu.exam.entity.QuestionAnswer;
import com.atguigu.exam.entity.QuestionChoice;
import com.atguigu.exam.exception.ExamException;
import com.atguigu.exam.mapper.PaperQuestionMapper;
import com.atguigu.exam.mapper.QuestionAnswerMapper;
import com.atguigu.exam.mapper.QuestionChoiceMapper;
import com.atguigu.exam.mapper.QuestionMapper;
import com.atguigu.exam.service.AIService;
import com.atguigu.exam.service.QuestionService;
import com.atguigu.exam.vo.AiGenerateRequestVo;
import com.atguigu.exam.vo.QuestionImportVo;
import com.atguigu.exam.vo.QuestionQueryDto;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 题目Service实现类
 * 实现题目相关的业务逻辑
 */
@Slf4j
@Service
public class QuestionServiceImpl extends ServiceImpl<QuestionMapper, Question> implements QuestionService {

    @Autowired
    private QuestionAnswerMapper questionAnswerMapper;

    @Autowired
    private QuestionChoiceMapper questionChoiceMapper;

    @Autowired
    private PaperQuestionMapper paperQuestionMapper;

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @Autowired
    private AIService aiService;

    @Override
    public Page<Question> getPageByCondition(Page<Question> questionPage, QuestionQueryDto queryDto) {
        //调用baseMapper中的分页及带条件的查询
        return baseMapper.getPageByCondition(questionPage, queryDto);
    }

    @Override
    public Page<Question> getPageByConditionWithStream(Page<Question> questionPage, QuestionQueryDto queryDto) {
        //创建LambdaQueryWrapper,用于构建查询条件
        LambdaQueryWrapper<Question> questionLambdaQueryWrapper = new LambdaQueryWrapper<>();
        //条件查询,queryDto中的参数,条件查询需要进行条件的判断,
        questionLambdaQueryWrapper.eq(ObjectUtils.isNotEmpty(queryDto.getCategoryId()), Question::getCategoryId, queryDto.getCategoryId());
        questionLambdaQueryWrapper.eq(ObjectUtils.isNotEmpty(queryDto.getDifficulty()), Question::getDifficulty, queryDto.getDifficulty());
        questionLambdaQueryWrapper.eq(ObjectUtils.isNotEmpty(queryDto.getType()), Question::getType, queryDto.getType());
        questionLambdaQueryWrapper.like(ObjectUtils.isNotEmpty(queryDto.getKeyword()), Question::getTitle, queryDto.getKeyword());
        questionLambdaQueryWrapper.orderByDesc(Question::getCreateTime);
        //调用baseMapper中的selectPage方法进行分页查询
        questionPage = baseMapper.selectPage(questionPage, questionLambdaQueryWrapper);
        //获取当前页对应的集合
        List<Question> pageQuestionList = questionPage.getRecords();
        //判断当前页对应的集合是否为空
        if (!CollectionUtils.isEmpty(pageQuestionList)) {
            //如果不为空就进行填充答案和选项,答案是每道题必须的,但是题目是选择题时才填充选项
            fillAnswerAndChoices(pageQuestionList);
        }
        return questionPage;
    }

    @Override
    public Question getQuestionDetailsById(Long id) {
        Question question = baseMapper.getQuestionDetailsById(id);
        //在这里需要处理热门题目的业务,点击一次查看详情就添加一次,并且使用redis中的zset使用score进行计数来判断是否是热门题目
        //开启一个新线程不影响主线程的查询
        new Thread(() -> {
            //向Redis中添加当前题目访问的次数
            redisTemplate.opsForZSet().incrementScore("question:popular", id, 1.0);
        }).start();
        return question;

    }

    //开启事务
    @Transactional
    @Override
    public void createQuestion(Question question) {
        //判断当前类型下是否存在该题目
        boolean exists = baseMapper.exists(new LambdaQueryWrapper<Question>().eq(Question::getType, question.getType()).eq(Question::getTitle, question.getTitle()));
        if (exists) {
            //表明已经存在,抛异常
            throw new ExamException(444, "在<" + question.getTitle() + ">类型中已经存在该题目");
        }
        //调用baseMapper中的添加方法
        baseMapper.insert(question);
        //获取question的答案
        QuestionAnswer answer = question.getAnswer();
        //获取数据库自增的题目id
        Long questionId = question.getId();
        //为answer设置questionId
        answer.setQuestionId(questionId);
        //判断是不是选择题
        if ("CHOICE".equals(question.getType())) {
            //获取所有选项
            List<QuestionChoice> choices = question.getChoices();
            //创建一个StringBuilder,用于拼接答案
            StringBuilder choiceContent = new StringBuilder();
            //遍历choice,将设置对应的答案选项
            for (int i = 0; i < choices.size(); i++) {
                //获取选项
                QuestionChoice choice = choices.get(i);
                //设置choice的questionId
                choice.setQuestionId(questionId);
                //对choice进行排序
                choice.setSort(i);
                //如果是选择题则保存数据
                questionChoiceMapper.insert(choice);
                //判断是不是正确答案,如果是进行
                if (choice.getIsCorrect()) {
                    //进入if表明是正确答案
                    if (choiceContent.length() > 0) {
                        choiceContent.append(",");
                    }
                    //进行添加选项字段
                    choiceContent.append((char) ('A' + i));
                }
            }
            //将正确答案设置到answer中
            answer.setAnswer(choiceContent.toString());
        }
        //3.保存答案
        questionAnswerMapper.insert(answer);
    }

    //开启事务
    @Transactional
    @Override
    public void updateQuestion(Question question) {
        //首先进行判断,判断更新完成后是否和原有的question的title相同,并且不是自己本身,在同一个类型下
        LambdaQueryWrapper<Question> questionLambdaQueryWrapper = new LambdaQueryWrapper<>();
        //构建查询条件
        questionLambdaQueryWrapper.eq(Question::getType, question.getType());
        questionLambdaQueryWrapper.ne(Question::getId, question.getId());
        questionLambdaQueryWrapper.eq(Question::getTitle, question.getTitle());
        //调用baseMapper中的exists方法,查看是否存在这样的题目
        boolean exists = baseMapper.exists(questionLambdaQueryWrapper);
        //进行判断
        if (exists) {
            //表示存在这样的题目,不能修改,抛异常
            throw new ExamException(555, "在<" + question.getTitle() + ">类型中已经存在该题目,请重新输入要修改的题目");
        }
        //进行修改操作
        baseMapper.updateById(question);
        //接下来修改答案和选项,首先获取答案,answer不用设置id,因为传入的question下包含questionId字段
        QuestionAnswer answer = question.getAnswer();
        //判断是不是选择题
        if ("CHOICE".equals(question.getType())) {
            //这里的更新选项的操作是将之前所有的选项删除,然后添加新的选项
            questionChoiceMapper.delete(new LambdaQueryWrapper<QuestionChoice>().eq(QuestionChoice::getQuestionId, question.getId()));
            //再次进行添加操作,获取所有的选项
            List<QuestionChoice> choices = question.getChoices();
            //创建一个StringBuilder对象用于拼接选择题的答案
            StringBuilder choiceContent = new StringBuilder();
            //遍历
            for (int i = 0; i < choices.size(); i++) {
                //获取每一个选项
                QuestionChoice choice = choices.get(i);
                //每个选项还有回显,需要将选项的id设置为null,以避免id冲突
                choice.setId(null);
                //将choice的create_time和update_time设置为空
                choice.setCreateTime(null);
                choice.setUpdateTime(null);
                //给选项排序
                choice.setSort(i);
                //调用questionChoiceMapper中的insert,将选择题的选项添加进去
                questionChoiceMapper.insert(choice);
                //接下来设置正确的答案
                if (choice.getIsCorrect()) {
                    //如果是正确答案,就将答案进行转换,并使用choiceContent进行拼接
                    if (choiceContent.length() > 0) {
                        //如果choiceContent下有数据就在后面加上,没有数据就不加
                        choiceContent.append(",");
                    }
                    //拼接选项字段
                    choiceContent.append((char) ('A' + i));
                }
            }
            //设置正确答案
            answer.setAnswer(choiceContent.toString());
        }
        //调用questionAnswerMapper修改答案
        questionAnswerMapper.updateById(answer);
    }

    @Transactional
    @Override
    public Boolean deleteQuestion(Long id) {
        //判断题目是否存在于试卷中,获取记录数,判断试卷下是否有题目
        Long count = paperQuestionMapper.selectCount(new LambdaQueryWrapper<PaperQuestion>().eq(PaperQuestion::getQuestionId, id));
        if (count > 0) {
            //表明题目存在试卷下,抛异常
            throw new ExamException(666, "当前题目已经被试卷使用，不能删除");
        }
        //删除题目
        baseMapper.delete(new LambdaQueryWrapper<Question>().eq(Question::getId, id));
        //删除题目下的答案
        questionAnswerMapper.delete(new LambdaQueryWrapper<QuestionAnswer>().eq(QuestionAnswer::getQuestionId, id));
        //删除题目下选择题的选项,如果不是选择题也走这个,没有选项就不进行操作
        questionChoiceMapper.delete(new LambdaQueryWrapper<QuestionChoice>().eq(QuestionChoice::getQuestionId, id));
        return true;
    }

    @Override
    public List<Question> getPopularQuestion(Integer size) {
        //通过redis获取热门的题目,跟据key获取value,value是每个题目的id
        Set<Object> redisQuestionIds = redisTemplate.opsForZSet().reverseRange("question:popular", 0, size - 1);
        //创建集合存放热门题目
        List<Question> returnList = new ArrayList<>();
        //判断redisQuestionIds是不是为null
        if (redisQuestionIds != null && redisQuestionIds.size() > 0) {
            //获取热门题目
            redisQuestionIds.forEach(questionId -> {
                //查询热门题目
                Question question = baseMapper.getQuestionDetailsById(Long.valueOf(questionId.toString()));
                //将热门题目添加到returnList
                returnList.add(question);
            });
        }

        //当热门题目不满足size个数时,我们需要补充最新的题目,先获取热门题目的数量
        int redisSize = redisQuestionIds.size();
        //计算差值,看还需要补充多少数据
        int diff = size - redisSize;
        //对diff进行判断,diff>0表明需要补充
        if (diff > 0) {
            //证明Redis中的热门题目数量不够，将数据库中最新的查询出来补全热门题目
            //创建LambdaQueryWrapper对象，用于构建查询条件
            LambdaQueryWrapper<Question> questionLambdaQueryWrapper = new LambdaQueryWrapper<>();
            //根据创建时间倒序排序获取最新的数据
            questionLambdaQueryWrapper.orderByDesc(Question::getCreateTime);
            //并且保证不是在redis热门题目当中的,给条件判断redisQuestionIds是否为空
            questionLambdaQueryWrapper.notIn(redisQuestionIds.size() > 0, Question::getId, redisQuestionIds);
            //需要限制数量,使用last在后面拼接limit
            questionLambdaQueryWrapper.last("limit " + diff);
            //进行查询获取需要进行添加的集合
            List<Question> diffQuestions = baseMapper.selectList(questionLambdaQueryWrapper);
            //将填充的diffQuestions加到热门题目中
            returnList.addAll(diffQuestions);
        }
        return returnList;
    }

    @Override
    public int batchImportQuestions(List<QuestionImportVo> questions) {
        //转换QuestionImportVo转换question
        //验证是否为空
        if (CollectionUtils.isEmpty(questions)) {
            throw new ExamException(999, "导入的题目不能为空");
        }
        //定义变量用于记录导入成功的数
        int successCount = 0;
        //遍历questions
        for (QuestionImportVo questionImportVo : questions) {
            //创建question对象
            Question question = new Question();
            //将questionImportVo的基本属性赋值给question
            BeanUtils.copyProperties(questionImportVo, question);
            //判断是否是选择题
            if ("CHOICE".equals(questionImportVo.getType())) {
                //获取所有的选项
                List<QuestionImportVo.ChoiceImportDto> choiceImportDto = questionImportVo.getChoices();
                //创建一个集合用于保存所有的选项
                List<QuestionChoice> choiceList = new ArrayList<>();
                //遍历choiceImportDto对象
                for (QuestionImportVo.ChoiceImportDto importDto : choiceImportDto) {
                    //创建QuestionChoice
                    QuestionChoice choice = new QuestionChoice();
                    //将questionImportVo中的ChoiceImportDto的属性赋值给choice
                    BeanUtils.copyProperties(importDto, choice);
                    //将转换的数据保存到choiceList中
                    choiceList.add(choice);
                }
                //将转换后的选项字段设置到question中
                question.setChoices(choiceList);
            }
            //设置答案字段
            QuestionAnswer questionAnswer = new QuestionAnswer();
            //判断是不是判断题,如果是将true转为大写
            questionAnswer.setAnswer("JUDGE".equals(questionImportVo.getType()) ? questionImportVo.getAnswer().toUpperCase() : questionImportVo.getAnswer());
            //将questionAnswer设置到question中
            question.setAnswer(questionAnswer);
            //进行批量添加操作
            try {
                createQuestion(question);
                successCount++;
            } catch (Exception e) {
                throw new ExamException(1000, "第" + successCount + 1 + "题<" + question.getTitle() + ">插入失败，失败原因：" + e.getMessage());
            }
        }
        return successCount;
    }

    @Override
    public List<QuestionImportVo> generateQuestionsByAi(AiGenerateRequestVo request) {
        //将提示词转换
        String prompt = buildPrompt(request);
        //调用kimiApi返回Ai生成的结果
        String content = aiService.callKimiAi(prompt);
        //4. 结果内容解析
    /*
           ```json
              {
                 questions:[{},{},{}]
              }
           ```
         */
        int startIndex = content.indexOf("```json");
        int endIndex = content.lastIndexOf("```");
        //保证有数据，且下标正确！
        if (startIndex != -1 && endIndex != -1 && startIndex < endIndex) {
            //获取真正结果
            String realResult = content.substring(startIndex + 7, endIndex);
            System.out.println("realResult = " + realResult);
            JSONObject jsonObject = JSONObject.parseObject(realResult);
            JSONArray questions = jsonObject.getJSONArray("questions");
            List<QuestionImportVo> questionImportVoList = new ArrayList<>();
            for (int i = 0; i < questions.size(); i++) {
                //获取对象
                JSONObject questionJson = questions.getJSONObject(i);
                QuestionImportVo questionImportVo = new QuestionImportVo();
                questionImportVo.setTitle(questionJson.getString("title"));
                questionImportVo.setType(questionJson.getString("type"));
                questionImportVo.setMulti(questionJson.getBoolean("multi"));
                questionImportVo.setDifficulty(questionJson.getString("difficulty"));
                questionImportVo.setScore(questionJson.getInteger("score"));
                questionImportVo.setAnalysis(questionJson.getString("analysis"));
                questionImportVo.setCategoryId(request.getCategoryId());

                //选择题处理选项
                if ("CHOICE".equals(questionImportVo.getType())) {
                    JSONArray choices = questionJson.getJSONArray("choices");
                    List<QuestionImportVo.ChoiceImportDto> choiceImportDtoList = new ArrayList<>(choices.size());
                    for (int i1 = 0; i1 < choices.size(); i1++) {
                        JSONObject choicesJSONObject = choices.getJSONObject(i1);
                        QuestionImportVo.ChoiceImportDto choiceImportDto = new QuestionImportVo.ChoiceImportDto();
                        choiceImportDto.setContent(choicesJSONObject.getString("content"));
                        choiceImportDto.setIsCorrect(choicesJSONObject.getBoolean("isCorrect"));
                        choiceImportDto.setSort(choicesJSONObject.getInteger("sort"));
                        choiceImportDtoList.add(choiceImportDto);
                    }
                    questionImportVo.setChoices(choiceImportDtoList);
                }
                //答案 [判断题！ TRUE |FALSE  false true  f  t 是 否]
                questionImportVo.setAnswer(questionJson.getString("answer"));
                questionImportVoList.add(questionImportVo);
            }
            return questionImportVoList;
        }
        throw new RuntimeException("ai生成题目json数据结构错误，无法正常解析！数据为：%s".formatted(content));
    }

    @Override
    public String buildPrompt(AiGenerateRequestVo request) {
        StringBuilder prompt = new StringBuilder();

        prompt.append("请为我生成").append(request.getCount()).append("道关于【")
                .append(request.getTopic()).append("】的题目。\n\n");

        prompt.append("要求：\n");

        // 题目类型要求
        if (request.getTypes() != null && !request.getTypes().isEmpty()) {
            List<String> typeList = Arrays.asList(request.getTypes().split(","));
            prompt.append("- 题目类型：");
            for (String type : typeList) {
                switch (type.trim()) {
                    case "CHOICE":
                        prompt.append("选择题");
                        if (request.getIncludeMultiple() != null && request.getIncludeMultiple()) {
                            prompt.append("(包含单选和多选)");
                        }
                        prompt.append(" ");
                        break;
                    case "JUDGE":
                        prompt.append("判断题（**重要：确保正确答案和错误答案的数量大致平衡，不要全部都是正确或错误**） ");
                        break;
                    case "TEXT":
                        prompt.append("简答题 ");
                        break;
                }
            }
            prompt.append("\n");
        }

        // 难度要求
        if (request.getDifficulty() != null) {
            String difficultyText = switch (request.getDifficulty()) {
                case "EASY" -> "简单";
                case "MEDIUM" -> "中等";
                case "HARD" -> "困难";
                default -> "中等";
            };
            prompt.append("- 难度等级：").append(difficultyText).append("\n");
        }

        // 额外要求
        if (request.getRequirements() != null && !request.getRequirements().isEmpty()) {
            prompt.append("- 特殊要求：").append(request.getRequirements()).append("\n");
        }

        // 判断题特别要求
        if (request.getTypes() != null && request.getTypes().contains("JUDGE")) {
            prompt.append("- **判断题特别要求**：\n");
            prompt.append("  * 确保生成的判断题中，正确答案(TRUE)和错误答案(FALSE)的数量尽量平衡\n");
            prompt.append("  * 不要所有判断题都是正确的或都是错误的\n");
            prompt.append("  * 错误的陈述应该是常见的误解或容易混淆的概念\n");
            prompt.append("  * 正确的陈述应该是重要的基础知识点\n");
        }

        prompt.append("\n请严格按照以下JSON格式返回，不要包含任何其他文字：\n");
        prompt.append("```json\n");
        prompt.append("{\n");
        prompt.append("  \"questions\": [\n");
        prompt.append("    {\n");
        prompt.append("      \"title\": \"题目内容\",\n");
        prompt.append("      \"type\": \"CHOICE|JUDGE|TEXT\",\n");
        prompt.append("      \"multi\": true/false,\n");
        prompt.append("      \"difficulty\": \"EASY|MEDIUM|HARD\",\n");
        prompt.append("      \"score\": 5,\n");
        prompt.append("      \"choices\": [\n");
        prompt.append("        {\"content\": \"选项内容\", \"isCorrect\": true/false, \"sort\": 1}\n");
        prompt.append("      ],\n");
        prompt.append("      \"answer\": \"TRUE或FALSE(判断题专用)|文本答案(简答题专用)\",\n");
        prompt.append("      \"analysis\": \"题目解析\"\n");
        prompt.append("    }\n");
        prompt.append("  ]\n");
        prompt.append("}\n");
        prompt.append("```\n\n");

        prompt.append("注意：\n");
        prompt.append("1. 选择题必须有choices数组，判断题和简答题设置answer字段\n");
        prompt.append("2. 多选题的multi字段设为true，单选题设为false\n");
        prompt.append("3. **判断题的answer字段只能是\"TRUE\"或\"FALSE\"，请确保答案分布合理**\n");
        prompt.append("4. 每道题都要有详细的解析\n");
        prompt.append("5. 题目要有实际价值，贴近实际应用场景\n");
        prompt.append("6. 严格按照JSON格式返回，确保可以正确解析\n");

        // 如果只生成判断题，额外强调答案平衡
        if (request.getTypes() != null && request.getTypes().equals("JUDGE") && request.getCount() > 1) {
            prompt.append("7. **判断题答案分布要求**：在").append(request.getCount()).append("道判断题中，");
            int halfCount = request.getCount() / 2;
            if (request.getCount() % 2 == 0) {
                prompt.append("请生成").append(halfCount).append("道正确(TRUE)和").append(halfCount).append("道错误(FALSE)的题目");
            } else {
                prompt.append("请生成约").append(halfCount).append("-").append(halfCount + 1).append("道正确(TRUE)和约").append(halfCount).append("-").append(halfCount + 1).append("道错误(FALSE)的题目");
            }
        }
        return prompt.toString();
    }

    //为题目填充答案和题目
    @Override
    public void fillAnswerAndChoices(List<Question> pageQuestionList) {
        //获取所有题目对应的id字段
        List<Long> ids = pageQuestionList.stream().map(Question::getId).collect(Collectors.toList());
        //根据ids查询到答案
        LambdaQueryWrapper<QuestionAnswer> questionAnswerLambdaQueryWrapper = new LambdaQueryWrapper<>();
        questionAnswerLambdaQueryWrapper.in(QuestionAnswer::getQuestionId, ids);
        //调用questionAnswerMapper中的selectList查询答案
        List<QuestionAnswer> questionAnswers = questionAnswerMapper.selectList(questionAnswerLambdaQueryWrapper);
        //根据ids查询选项
        LambdaQueryWrapper<QuestionChoice> questionChoiceLambdaQueryWrapper = new LambdaQueryWrapper<>();
        questionChoiceLambdaQueryWrapper.in(QuestionChoice::getQuestionId, ids);
        //调用questionChoiceMapper中的selectList查询选项
        List<QuestionChoice> questionChoices = questionChoiceMapper.selectList(questionChoiceLambdaQueryWrapper);
        //将questionAnswers转换成map,以questionId为key,以QuestionAnswer为value
        Map<Long, QuestionAnswer> questionAnswerMap = questionAnswers.stream().collect(Collectors.toMap(QuestionAnswer::getQuestionId, q -> q));
        //将questionChoices转换成map,根据questionId进行分组,获取map,key为questionId,List<QuestionChoice>为value
        Map<Long, List<QuestionChoice>> choiceMap = questionChoices.stream().collect(Collectors.groupingBy(QuestionChoice::getQuestionId));
        //遍历pageQuestionList,为每个题目填充答案和选项
        pageQuestionList.forEach(question -> {
            //获取答案
            QuestionAnswer questionAnswer = questionAnswerMap.get(question.getId());
            //每个题目都有答案,填充答案
            question.setAnswer(questionAnswer);
            //先进行判断,判断题目类型,选择题和判断题才有选项
            if ("CHOICE".equals(question.getType())) {
                //根据id获取所有选项
                List<QuestionChoice> choices = choiceMap.get(question.getId());
                //对选项进行排序
                choices.sort((c1, c2) -> c1.getSort().compareTo(c2.getSort()));
                //给question填充choice
                question.setChoices(choices);
            }
        });
    }
}